`timescale 1ns / 1ps

`include "data_width.vh"

module multiplexer #(parameter
    VERTEX_BRAM_DWIDTH = `VERTEX_BRAM_DWIDTH,
    EDGE_PIPE_NUM = `EDGE_PIPE_NUM, VERTEX_PIPE_NUM = `VERTEX_PIPE_NUM,
    DST_ID_DWIDTH = `DST_ID_DWIDTH, VERTEX_MASK_WIDTH = `VERTEX_MASK_WIDTH
    ) (
        input                                                   clk,
        input                                                   front_rst,
        input [VERTEX_BRAM_DWIDTH * EDGE_PIPE_NUM - 1 : 0]      front_src,
        input                                                   front_src_valid,
        input [DST_ID_DWIDTH * VERTEX_PIPE_NUM - 1 : 0]         front_dst_id,
        input [VERTEX_MASK_WIDTH * VERTEX_PIPE_NUM - 1 : 0]     front_src_mask,
        input [VERTEX_PIPE_NUM - 1 : 0]                         front_dst_data_valid,

        output                                                  rst,
        output [DST_ID_DWIDTH * VERTEX_PIPE_NUM - 1 : 0]        dst_id,
        output [VERTEX_BRAM_DWIDTH * VERTEX_PIPE_NUM - 1 : 0]   src,
        output [VERTEX_PIPE_NUM - 1 : 0]                        dst_data_valid);
    
    multiplexer_para_trans P (
        .clk(clk), .front_rst(front_rst),

        .rst(rst));

    multiplexer_edge E0
        (.clk(clk), .rst(front_rst),
          .front_src(front_src), .front_src_valid(front_src_valid),
          .front_src_mask(front_src_mask),
          
          .src(src));
        
    generate
        genvar i;
        for (i = 0; i < VERTEX_PIPE_NUM; i = i + 1) begin : M15_BLOCK_1
            multiplexer_vertex_pipeline P (
                .clk(clk), .rst(front_rst),
                .front_dst_id(front_dst_id[(i + 1) * DST_ID_DWIDTH - 1 : i * DST_ID_DWIDTH]),
                .front_dst_data_valid(front_dst_data_valid[i]),

                .dst_id(dst_id[(i + 1) * DST_ID_DWIDTH - 1 : i * DST_ID_DWIDTH]),
                .dst_data_valid(dst_data_valid[i]));
        end
    endgenerate

endmodule

module multiplexer_para_trans (
    input       clk,
    input       front_rst,

    output reg  rst);

    always @ (posedge clk) begin
        rst <= front_rst;
    end

endmodule

module multiplexer_edge #(parameter
    VERTEX_BRAM_DWIDTH = `VERTEX_BRAM_DWIDTH,
    EDGE_PIPE_NUM = `EDGE_PIPE_NUM,
    VERTEX_MASK_WIDTH = `VERTEX_MASK_WIDTH,
    VERTEX_PIPE_NUM = `VERTEX_PIPE_NUM
    ) (
    input                                                   clk,
    input                                                   rst,
    input [VERTEX_BRAM_DWIDTH * EDGE_PIPE_NUM - 1 : 0]      front_src,
    input                                                   front_src_valid,
    input [VERTEX_MASK_WIDTH * VERTEX_PIPE_NUM - 1 : 0]     front_src_mask,

    output [VERTEX_BRAM_DWIDTH * VERTEX_PIPE_NUM - 1 : 0]   src);

    generate
        genvar i;
        for (i = 0; i < VERTEX_PIPE_NUM; i = i + 1) begin : M15_BLOCK_2
            multiplexer_edge_single S (
                .clk(clk), .rst(rst),
                .front_src(front_src), .front_src_valid(front_src_valid),
                .front_src_mask(front_src_mask[(i + 1) * VERTEX_MASK_WIDTH - 1 : i * VERTEX_MASK_WIDTH]),

                .src(src[(i + 1) * VERTEX_BRAM_DWIDTH - 1 : i * VERTEX_BRAM_DWIDTH]));
        end
    endgenerate

endmodule

module multiplexer_edge_single #(parameter
    VERTEX_BRAM_DWIDTH = `VERTEX_BRAM_DWIDTH,
    EDGE_PIPE_NUM = `EDGE_PIPE_NUM,
    VERTEX_MASK_WIDTH = `VERTEX_MASK_WIDTH
) (
    input                                               clk,
    input                                               rst,
    input [VERTEX_BRAM_DWIDTH * EDGE_PIPE_NUM - 1 : 0]  front_src,
    input                                               front_src_valid,
    input [VERTEX_MASK_WIDTH - 1 : 0]                   front_src_mask,
    
    output reg [VERTEX_BRAM_DWIDTH - 1 : 0]             src);

    always @ (posedge clk) begin
        if (rst) begin
            src <= 0;
        end
        else begin
            if (front_src_valid == 1'b1) begin
                case (front_src_mask)
                    5'b00000: src <= front_src[VERTEX_BRAM_DWIDTH - 1 : 0];
                    5'b00001: src <= front_src[VERTEX_BRAM_DWIDTH * 2 - 1 : VERTEX_BRAM_DWIDTH];
                    5'b00010: src <= front_src[VERTEX_BRAM_DWIDTH * 3 - 1 : VERTEX_BRAM_DWIDTH * 2];
                    5'b00011: src <= front_src[VERTEX_BRAM_DWIDTH * 4 - 1 : VERTEX_BRAM_DWIDTH * 3];
                    5'b00100: src <= front_src[VERTEX_BRAM_DWIDTH * 5 - 1 : VERTEX_BRAM_DWIDTH * 4];
                    5'b00101: src <= front_src[VERTEX_BRAM_DWIDTH * 6 - 1 : VERTEX_BRAM_DWIDTH * 5];
                    5'b00110: src <= front_src[VERTEX_BRAM_DWIDTH * 7 - 1 : VERTEX_BRAM_DWIDTH * 6];
                    5'b00111: src <= front_src[VERTEX_BRAM_DWIDTH * 8 - 1 : VERTEX_BRAM_DWIDTH * 7];
                    5'b01000: src <= front_src[VERTEX_BRAM_DWIDTH * 9 - 1 : VERTEX_BRAM_DWIDTH * 8];
                    5'b01001: src <= front_src[VERTEX_BRAM_DWIDTH * 10 - 1 : VERTEX_BRAM_DWIDTH * 9];
                    5'b01010: src <= front_src[VERTEX_BRAM_DWIDTH * 11 - 1 : VERTEX_BRAM_DWIDTH * 10];
                    5'b01011: src <= front_src[VERTEX_BRAM_DWIDTH * 12 - 1 : VERTEX_BRAM_DWIDTH * 11];
                    5'b01100: src <= front_src[VERTEX_BRAM_DWIDTH * 13 - 1 : VERTEX_BRAM_DWIDTH * 12];
                    5'b01101: src <= front_src[VERTEX_BRAM_DWIDTH * 14 - 1 : VERTEX_BRAM_DWIDTH * 13];
                    5'b01110: src <= front_src[VERTEX_BRAM_DWIDTH * 15 - 1 : VERTEX_BRAM_DWIDTH * 14];
                    5'b01111: src <= front_src[VERTEX_BRAM_DWIDTH * 16 - 1 : VERTEX_BRAM_DWIDTH * 15];
                    5'b10000: src <= front_src[VERTEX_BRAM_DWIDTH * 17 - 1 : VERTEX_BRAM_DWIDTH * 16];
                    5'b10001: src <= front_src[VERTEX_BRAM_DWIDTH * 18 - 1 : VERTEX_BRAM_DWIDTH * 17];
                    5'b10010: src <= front_src[VERTEX_BRAM_DWIDTH * 19 - 1 : VERTEX_BRAM_DWIDTH * 18];
                    5'b10011: src <= front_src[VERTEX_BRAM_DWIDTH * 20 - 1 : VERTEX_BRAM_DWIDTH * 19];
                    5'b10100: src <= front_src[VERTEX_BRAM_DWIDTH * 21 - 1 : VERTEX_BRAM_DWIDTH * 20];
                    5'b10101: src <= front_src[VERTEX_BRAM_DWIDTH * 22 - 1 : VERTEX_BRAM_DWIDTH * 21];
                    5'b10110: src <= front_src[VERTEX_BRAM_DWIDTH * 23 - 1 : VERTEX_BRAM_DWIDTH * 22];
                    5'b10111: src <= front_src[VERTEX_BRAM_DWIDTH * 24 - 1 : VERTEX_BRAM_DWIDTH * 23];
                    5'b11000: src <= front_src[VERTEX_BRAM_DWIDTH * 25 - 1 : VERTEX_BRAM_DWIDTH * 24];
                    5'b11001: src <= front_src[VERTEX_BRAM_DWIDTH * 26 - 1 : VERTEX_BRAM_DWIDTH * 25];
                    5'b11010: src <= front_src[VERTEX_BRAM_DWIDTH * 27 - 1 : VERTEX_BRAM_DWIDTH * 26];
                    5'b11011: src <= front_src[VERTEX_BRAM_DWIDTH * 28 - 1 : VERTEX_BRAM_DWIDTH * 27];
                    5'b11100: src <= front_src[VERTEX_BRAM_DWIDTH * 29 - 1 : VERTEX_BRAM_DWIDTH * 28];
                    5'b11101: src <= front_src[VERTEX_BRAM_DWIDTH * 30 - 1 : VERTEX_BRAM_DWIDTH * 29];
                    5'b11110: src <= front_src[VERTEX_BRAM_DWIDTH * 31 - 1 : VERTEX_BRAM_DWIDTH * 30];
                    5'b11111: src <= front_src[VERTEX_BRAM_DWIDTH * 32 - 1 : VERTEX_BRAM_DWIDTH * 31];
                endcase
            end
            else begin
                src <= 0;
            end
        end
    end
endmodule

module multiplexer_vertex_pipeline #(parameter
    DST_ID_DWIDTH = `DST_ID_DWIDTH
    ) (
    input                               clk,
    input                               rst,
    input [DST_ID_DWIDTH - 1 : 0]       front_dst_id,
    input                               front_dst_data_valid,

    output reg [DST_ID_DWIDTH - 1 : 0]  dst_id,
    output reg                          dst_data_valid);

    always @ (posedge clk) begin
        if (rst) begin
            dst_id          <= 0;
            dst_data_valid  <= 1'b0;
        end
        else begin
            dst_id          <= front_dst_id;
            dst_data_valid  <= front_dst_data_valid;
        end
    end

endmodule